package com.llyb.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.llyb.tingshu.account.client.UserAccountFeignClient;
import com.llyb.tingshu.album.client.AlbumInfoFeignClient;
import com.llyb.tingshu.album.client.TrackInfoFeignClient;
import com.llyb.tingshu.common.constant.SystemConstant;
import com.llyb.tingshu.common.execption.GuiguException;
import com.llyb.tingshu.common.rabbit.constant.MqConst;
import com.llyb.tingshu.common.rabbit.service.RabbitService;
import com.llyb.tingshu.common.result.Result;
import com.llyb.tingshu.common.result.ResultCodeEnum;
import com.llyb.tingshu.model.album.AlbumInfo;
import com.llyb.tingshu.model.album.TrackInfo;
import com.llyb.tingshu.model.order.OrderDerate;
import com.llyb.tingshu.model.order.OrderDetail;
import com.llyb.tingshu.model.order.OrderInfo;
import com.llyb.tingshu.model.user.VipServiceConfig;
import com.llyb.tingshu.order.helper.SignHelper;
import com.llyb.tingshu.order.mapper.OrderDerateMapper;
import com.llyb.tingshu.order.mapper.OrderDetailMapper;
import com.llyb.tingshu.order.mapper.OrderInfoMapper;
import com.llyb.tingshu.order.service.OrderInfoService;
import com.llyb.tingshu.user.client.UserInfoFeignClient;
import com.llyb.tingshu.vo.account.AccountDeductVo;
import com.llyb.tingshu.vo.order.OrderDerateVo;
import com.llyb.tingshu.vo.order.OrderDetailVo;
import com.llyb.tingshu.vo.order.OrderInfoVo;
import com.llyb.tingshu.vo.order.TradeVo;
import com.llyb.tingshu.vo.user.UserInfoVo;
import com.llyb.tingshu.vo.user.UserPaidRecordVo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        OrderInfo oneByOrderNo = findOneByOrderNo(orderNo);
        if (null != oneByOrderNo) {
            String orderStatusName = SystemConstant.ORDER_STATUS_UNPAID.equals(oneByOrderNo.getOrderStatus())? "待支付" :
                    SystemConstant.ORDER_STATUS_PAID.equals(oneByOrderNo.getOrderStatus())? "已支付" : "取消支付";
            oneByOrderNo.setOrderStatusName(orderStatusName);
            // 赋值订单明细
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, oneByOrderNo.getId()));
            //  赋值：
            oneByOrderNo.setOrderDetailList(orderDetailList);
        }
        return oneByOrderNo;
    }

    public OrderInfo findOneByOrderNo(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOne(new QueryWrapper<OrderInfo>().eq("order_no", orderNo));

        String payWayName = SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWayName())? "账户余额" : "在线支付";
        orderInfo.setPayWayName(payWayName);
        return orderInfo;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {

        // 1. 验签
        Map paramMap = JSONObject.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //      去掉后台未赋值的参数
        paramMap.put("payWay","");
        SignHelper.checkSign(paramMap);

        // 2. 去重 (使用lua脚本)
        String tradeNo = orderInfoVo.getTradeNo();
        String tradeNoKey = "user:trade:" + userId;
        String delScript = """
                if redis.call("get",KEYS[1]) == ARGV[1]
                then
                    return redis.call("del",KEYS[1])
                else
                    return 0
                end
                """;
        DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>();
        longDefaultRedisScript.setScriptText(delScript);
        longDefaultRedisScript.setResultType(Long.class);
        Long delFlag = (Long) redisTemplate.execute(longDefaultRedisScript, Arrays.asList(tradeNoKey), tradeNo);

        if (delFlag == 0) {
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");

        // 3. 判断支付类型
        /**
         * 1. 在线支付
         *    1.1 发送延迟消息
         * 2. 余额支付
         *    2.1 减去用户余额
         *    2.2 保存订单明细
         *    2.3 判断付款项目类型
         *      2.3.1 保存用户购买了哪些 专辑 或 声音 或 vip，入库
         */
        if (!SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
            // 在线支付( 包含微信、支付宝)
            saveOrderInfo(orderInfoVo, userId, orderNo);
            // 发送延迟消息
            rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER,MqConst.ROUTING_CANCEL_ORDER,orderNo, MqConst.CANCEL_ORDER_DELAY_TIME);
//            测试
//            rabbitService.sendDealyMessage(MqConst.EXCHANGE_ORDER,MqConst.ROUTING_CANCEL_ORDER,orderNo, 10);
        }
        else {
            // 余额支付
            // 远程调用 ，付钱
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
            // 远程调用来付钱
            Result<Boolean> resultR = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            Assert.notNull(resultR, "调用用户账户服务失败");
            Boolean result = resultR.getData();
            if (!result) {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_MINUSLOCK_ERROR);
            }

            // 保存订单
            saveOrderInfo(orderInfoVo, userId, orderNo);

            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.update(orderInfo, new QueryWrapper<OrderInfo>().eq("order_no", orderNo));


            // todo 保存用户购买了哪些专辑、声音、vip
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            userPaidRecordVo.setItemIdList(orderInfoVo
                    .getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).toList());

            //  远程调用：
            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            //  判断
            if (200 != userResult.getCode()) {
                throw new GuiguException(211, "新增购买记录异常");
            }

        }

        return orderNo;
    }

    public void saveOrderInfo(OrderInfoVo orderInfoVo, Long userId, String orderNo) {

        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        orderInfo.setOrderNo(orderNo);
        orderInfo.setUserId(userId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        orderInfoMapper.insert(orderInfo);

        // 订单明细
        if (!CollectionUtils.isEmpty(orderInfoVo.getOrderDetailVoList())) {
            orderInfoVo.getOrderDetailVoList().stream().forEach(orderDetailVo -> {
                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(orderDetailVo, orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            });
        }

        // 订单减免明细
        if (!CollectionUtils.isEmpty(orderInfoVo.getOrderDerateVoList())) {
            orderInfoVo.getOrderDerateVoList().stream().forEach(orderDerateVo -> {
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                orderDerateMapper.insert(orderDerate);
            });
        }

    }

    @Override
    public OrderInfoVo trade(TradeVo tradeVo,Long userId) {

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        // 赋值
        String tradeNo  = UUID.randomUUID().toString().replaceAll("-", "");
        // 防重：生成一个唯一标识，保存到redis中一份
        String tradeNoKey = "user:trade:" + userId;
        // 定义一个流水号
        redisTemplate.opsForValue().set(tradeNoKey, tradeNo);

        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOriginalAmount(new BigDecimal("0.00"));
        orderInfoVo.setDerateAmount(new BigDecimal("0.00"));
        orderInfoVo.setOrderAmount(new BigDecimal("0.00"));
        // 订单明细列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        // 订单折扣明细表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        // 远程调用，获取用户信息，判断用户身份
        Result<UserInfoVo> userInfoR = userInfoFeignClient.getUserInfo(userId);
        Assert.notNull(userInfoR, "获取用户信息失败");
        UserInfoVo userInfoVo = userInfoR.getData();
        Assert.notNull(userInfoVo, "获取用户信息失败,用户信息为空");

        // 赋值订单价格
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {

            //  判断用户是否购买过专辑
            Result<Boolean> isPaidAlbumResult = this.userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult, "获取用户是否购买专辑失败");
            Boolean isPaidAlbum = isPaidAlbumResult.getData();
            Assert.notNull(isPaidAlbum, "获取用户是否购买专辑失败,用户是否购买专辑为空");
            if (isPaidAlbum){
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }

            Result<AlbumInfo> albumInfoR = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoR, "获取专辑信息失败");
            AlbumInfo albumInfo = albumInfoR.getData();
            Assert.notNull(albumInfo, "获取专辑信息失败,专辑信息为空");

            orderInfoVo.setOriginalAmount(albumInfo.getPrice());

            BigDecimal derateAmount = new BigDecimal("0.00");
            // 专辑价格
            /**
             * 1. 用户是否会员
             *      1.1 是
             *          1.1.1 看 vip 折扣，有，显示会员价格
             *          1.1.2  没有，看是否有普通会员折扣，有，显示会员价格
             *      1.2 不是
             *          直接普通用户折扣
             */
            if (userInfoVo.getIsVip().equals(1) && userInfoVo.getVipExpireTime().after(new Date())) {
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("0"))>0) {
                    // 有vip折扣
                    // 优惠金额
                    derateAmount = albumInfo.getPrice().multiply((new BigDecimal("10")
                            .subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"),2,BigDecimal.ROUND_HALF_UP)
                            );
                    orderInfoVo.setDerateAmount(derateAmount);
                }else if (albumInfo.getDiscount().compareTo(new BigDecimal("0"))>0) {
                    derateAmount = albumInfo.getPrice().multiply((new BigDecimal("10")
                            .subtract(albumInfo.getDiscount())).divide(new BigDecimal("10"),2,BigDecimal.ROUND_HALF_UP)
                    );
                    orderInfoVo.setDerateAmount(derateAmount);
                }
            }else {
                if (albumInfo.getDiscount().compareTo(new BigDecimal("0"))>0) {
                    derateAmount = albumInfo.getPrice().multiply((new BigDecimal("10")
                            .subtract(albumInfo.getDiscount())).divide(new BigDecimal("10"),2,BigDecimal.ROUND_HALF_UP)
                    );
                    orderInfoVo.setDerateAmount(derateAmount);
                }
            }

            // 优惠价格大于0，订单价格减去优惠价格
            orderInfoVo.setOrderAmount(albumInfo.getPrice().subtract(derateAmount));

            // 订单列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(orderInfoVo.getOrderAmount());
            orderDetailVoList.add(orderDetailVo);

            // 订单减免列表
            if (derateAmount.compareTo(new BigDecimal("0"))>0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑折扣");
                orderDerateVoList.add(orderDerateVo);
            }

        }
        else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {

            // todo 声音类型
            // 原始价格，折扣价格，订单价格 ，订单列表，没有订单减免列表
            // 远程调用 专辑接口 获取单条声音的价格
            /**
             * 1 获取单个声音的价格
             * 2 传递声音id，数量
             * 3 排除掉用户已经购买的声音，获取剩余的声音对象集合
             */
            Result<TrackInfo> trackInfoR = trackInfoFeignClient.getTrackInfo(tradeVo.getItemId());
            Assert.notNull(trackInfoR, "获取单个声音价格失败");
            TrackInfo trackInfo = trackInfoR.getData();
            Assert.notNull(trackInfo, "获取单个声音价格失败,单个声音价格为空");

            // 获取单个声音价格
            Result<AlbumInfo> albumInfoR = albumInfoFeignClient.getAlbumInfo(trackInfo.getAlbumId());
            Assert.notNull(albumInfoR, "获取单个声音价格失败,专辑信息为空");
            AlbumInfo albumInfo = albumInfoR.getData();
            Assert.notNull(albumInfo, "获取单个声音价格失败,专辑信息为空");

            // 获取（排除掉已购买的声音）声音对象列表
            Result<List<TrackInfo>> paidTrackInfoListR = trackInfoFeignClient
                    .findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(paidTrackInfoListR, "获取（排除掉已购买的声音）声音对象列表失败");
            List<TrackInfo> paidTrackInfoList = paidTrackInfoListR.getData();
            Assert.notNull(paidTrackInfoList, "获取（排除掉已购买的声音）声音对象列表失败,已购买声音列表为空");

            orderInfoVo.setOriginalAmount(albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount())));
            // 单集购买声音无折扣
            orderInfoVo.setOrderAmount(albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount())));

            // 订单明细list
            for (TrackInfo info : paidTrackInfoList) {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(info.getId());
                orderDetailVo.setItemName(info.getTrackTitle());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemUrl(info.getCoverUrl());
                orderDetailVoList.add(orderDetailVo);
            }
            // 无订单减免列表


        }else {
            // vip会员
            Result<VipServiceConfig> vipServiceConfigR = userInfoFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigR, "获取VIP服务配置信息失败");
            VipServiceConfig vipServiceConfig = vipServiceConfigR.getData();
            Assert.notNull(vipServiceConfig, "获取VIP服务配置信息失败,VIP服务配置信息为空");

            orderInfoVo.setOriginalAmount(vipServiceConfig.getPrice());
            orderInfoVo.setDerateAmount(vipServiceConfig.getPrice().subtract(vipServiceConfig.getDiscountPrice()));
            orderInfoVo.setOrderAmount(vipServiceConfig.getDiscountPrice());

            // 订单列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(orderInfoVo.getOrderAmount());
            orderDetailVoList.add(orderDetailVo);

            // 订单减免列表
            if (vipServiceConfig.getPrice().subtract(vipServiceConfig.getDiscountPrice()).compareTo(new BigDecimal("0"))>0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(vipServiceConfig.getPrice().subtract(vipServiceConfig.getDiscountPrice()));
                orderDerateVo.setRemarks("VIP服务折扣");
                orderDerateVoList.add(orderDerateVo);
            }
        }




        // 订单列表
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        // 订单减免列表
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        orderInfoVo.setTimestamp(System.currentTimeMillis());

        // 防止被篡改
        orderInfoVo.setSign(SignHelper
                .getSign(JSONObject.parseObject(JSONObject.toJSONString(orderInfoVo), Map.class)));

        return orderInfoVo;
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> pageOrderList, Long userId, String orderStatus) {
        IPage<OrderInfo> orderInfoIPage = orderInfoMapper.selectUserPage(pageOrderList, userId, orderStatus);
        orderInfoIPage.getRecords().forEach(orderInfo -> {
            // 赋值payWayName 和 orderStatusName
            String orderStatusName = SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())? "待支付" :
                    SystemConstant.ORDER_STATUS_PAID.equals(orderInfo.getOrderStatus())? "已支付" : "取消支付";
            orderInfo.setOrderStatusName(orderStatusName);

            String payWayName = SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWayName())? "账户余额" : "在线支付";
            orderInfo.setPayWayName(payWayName);
        });
        return orderInfoIPage;
    }

    @Override
    public void orderPaySuccess(String orderNo) {
        OrderInfo orderInfoByOrderNo = this.getOrderInfoByOrderNo(orderNo);
        if (null != orderInfoByOrderNo && orderInfoByOrderNo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)) {
            orderInfoByOrderNo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            // 更新数据
            this.updateById(orderInfoByOrderNo);

            // 不要忘记，保存购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfoByOrderNo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfoByOrderNo.getUserId());
            userPaidRecordVo.setItemType(orderInfoByOrderNo.getItemType());
            List<Long> itemIdList = orderInfoByOrderNo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            userInfoFeignClient.savePaidRecord(userPaidRecordVo);
        }

    }

    /**
     * 订单取消
     *
     * @param orderId
     */
    @Override
    public void orderCancel(String orderNo) {
        //  取消订单：
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  判断是否为空：
        if (null == orderInfo) {
            log.info("订单不存在：{}", orderNo);
            return;
        }
        //  修改当前状态;
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)) {
            log.info("订单已支付：{}", orderNo);
            return;
        }
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
        this.updateById(orderInfo);
    }
}
